<template>
  <view class="page-layout ver-layout-center" :class="themeObj.titleClass" :style="themeObj.pageStyle">
    <view class="title-layout hor-layout-side-center">
      <view class="title-left-layout ver-layout-center-all" @click="openDrawer">
        <image class="title-left-icon" src="https://noss.douguguo.com/static/image/menu.png"></image>
      </view>
      <view class="title-view">New chat</view>
      <view class="title-right-view" @click="toPage(minePage)">个人中心</view>
    </view>
    <scroll-view v-show="showChatContent" scroll-y scroll-with-animation
                 :scroll-top="chatScrollTop" class="chat_content-layout ver-layout scroll-Y">
      <view v-for="(item,index) in messageArr" :key="index" :id="'chatItemView'+index" class="chat-item">
        <view v-if="item.create_time&&item.role=='user'" class="message-time">{{ item.create_time }}</view>
        <view v-if="item.role=='assistant'" class="hor-layout assistant-content">
          <image class="chat-user-icon" src="https://noss.douguguo.com/static/image/ai.png"></image>
          <pre>
              <view class="chat-content"
                    :class="[{'waiting':(index==messageArr.length-1)&&item.flag=='last'},themeObj.bottomInputClass]">
                <block v-for="(aItem,aIdx) in item.content" :key="aIdx">
                  <view v-if="aItem.type=='text'" class="chat-content-text">{{ aItem.data }}</view>
                  <view v-if="aItem.type=='code'" class="chat-code">
                    {{ aItem.data }}
                  </view>
                </block>
              </view>
            </pre>
        </view>
        <view v-else class="hor-layout use-content">
            <pre>
              <view class="chat-content" :class="themeObj.bottomInputClass">{{ item.content }}</view>
            </pre>
          <image class="chat-user-icon" src="https://noss.douguguo.com/static/image/user.png"></image>
        </view>
      </view>
    </scroll-view>
    <view v-show="!showChatContent" :class="{'bounceInUpSoon':!showChatContent}" class="demo-content-layout">
      <view class="main-title" :class="themeObj.titleClass">
        Chat Ai
      </view>
      <view class="ver-layout" v-for="(item,index) in textList">
        <view class="hor-layout-center-all" style="margin-bottom: 15px">
          <image class="demo-icon" :src="themeObj[item.textIcon]"></image>
          <view :class="themeObj.titleClass">{{ item.title }}</view>
        </view>
        <view v-for="(text,index) in item.list" :key="index" :class="themeObj.textItemClass"
              class="hor-layout-center-all text-item">
          <view @click="textItemClick(item,text)">{{ text }}</view>
          <image v-if="item.title=='使用示例'" class="text-icon"
                 src="https://noss.douguguo.com/static/image/more.png"></image>
        </view>
      </view>
    </view>
    <view class="bottom-chat-layout hor-layout-center-all" :class="themeObj.bottomClass">
      <input v-model="inputValue" :disabled="sendDisabled" type="textarea" class="chat-input"
             :class="themeObj.bottomInputClass" @keydown.enter="sendMessage">
      <div v-show="sendDisabled" class="dot-flashing"></div>
      <image v-show="!sendDisabled" class="bottom-input-icon" :src="themeObj.bottomInputIconSrc"
             @click="sendMessage"></image>
    </view>
    <uni-drawer ref="drawer" mode="left" :width="300">
      <view class="ver-layout drawer-layout">
        <view class="new-chat-layout hor-layout-side-center">
          <view class="new-chat-title">新的对话</view>
          <image class="add-icon" src="https://noss.douguguo.com/static/image/add.png"></image>
        </view>
        <view style="height: 60%">
          <view class="chat-questions single-line-text" v-for="(item,index) in myQuestions" :key="index">
            <view>{{ item.content }}</view>
          </view>
        </view>
        <view class="ver-layout drawer-setting-layout">
          <view class="hor-layout-center drawer-setting-item" @click="settingItemClick('clear')">
            <image class="setting-icon" src="https://noss.douguguo.com/static/image/del.png"></image>
            <view class="setting-text">清除对话</view>
          </view>
          <view class="hor-layout-center drawer-setting-item" @click="settingItemClick('question')">
            <image class="setting-icon" src="https://noss.douguguo.com/static/image/help.png"></image>
            <view class="setting-text">常见问题</view>
          </view>
          <view class="hor-layout-center drawer-setting-item" @click="settingItemClick('mode')">
            <block v-if="appTheme=='white'">
              <image class="setting-icon" src="https://noss.douguguo.com/static/image/month.png"></image>
              <view class="setting-text">夜间模式</view>
            </block>
            <block v-else>
              <image class="setting-icon" src="https://noss.douguguo.com/static/image/sun.png"></image>
              <view class="setting-text">日间模式</view>
            </block>
          </view>
        </view>
      </view>
    </uni-drawer>
  </view>
</template>

<script>
import {http} from "../../utils/request";
import {minePage} from "../../router"
import {textList, themeData} from "./themeData"

export default {
  name: "openai",
  data() {
    return {
      themeData,
      textList,
      inputValue: '',
      appTheme: '',
      sendDisabled: false,
      showChatContent: false,
      chatList: [],
      messageList: [
        // {role: 'user', content: '用js绘制一个动态图形'},
        // {
        //   role: 'assistant',
        //   content: "要绘制一个动态图形，常用的工具是Canvas和SVG，这里我们使用Canvas来实现。\n\n代码实现如下：\n\nHTML部分：\n\n```html\n<canvas id=\"canvas\"></canvas>\n```\n\nJS部分：\n\n```javascript\nconst canvas = document.getElementById(\"canvas\"); // 获取canvas元素\nconst ctx = canvas.getContext(\"2d\"); // 获取绘图上下文\n\n// 初始化圆形的位置和大小参数\nlet x = 50;\nlet y = 50;\nlet r = 20;\n\n// 设置每一帧动画的更新函数\nfunction update() {\n  ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布\n\n  // 绘制圆形\n  ctx.beginPath();\n  ctx.arc(x, y, r, 0, Math.PI * 2);\n  ctx.fillStyle = \"red\";\n  ctx.fill();\n\n  // 更新圆形的位置和大小\n  x += 2;\n  y += 2;\n  r += 1;\n\n  // 在下一帧执行更新函数\n  requestAnimationFrame(update);\n}\n\n// 开始动画\nrequestAnimationFrame(update);\n```\n\n这个例子中，我们使用Canvas的`getContext`方法获取了绘图上下文对象，在动画的每一帧中，我们使用`clearRect`方法清空画布，并绘制一个圆形。每一帧结束后，我们调用`requestAnimationFrame`方法，在下一帧执行更新函数，实现了一个简单的动画效果。"
        // },
        // {role: 'user', content: '用js绘制一个动态图形'},
      ],
      targetView: '',
      chatScrollTop: 10000,
      ipInfo: '',
      systemInfo: '',
      minePage
    }
  },
  computed: {
    themeObj() {
      let theme = this.appTheme
      let data = this.themeData[theme]
      return data
    },
    messageArr() {
      let list = []
      this.messageList.forEach(item => {
        let content = item.content.replace(/\n\n/g, "\n")
        if (item.role == 'assistant') content = this.convertStrList(content)
        list.push({...item, content})
      })
      return list
    },
    myQuestions() {
      return this.messageList.filter(item => item.role == 'user')
    }
  },
  watch: {
    nowPhoneHeight: {
      handler(newVal) {
      }
    },
    showChatContent: {
      handler(newVal) {
      },
      immediate: true
    }
  },
  onLoad() {
    uni.setStorageSync('theme', 'dark')
    this.appTheme = uni.getStorageSync("theme")
    this.initListener()
  },
  onShow(options) {
  },
  methods: {
    toPage(url) {
      this.$tools.pageGo(url)
    },
    initListener() {
      uni.getSystemInfo({
        success: res => {
          this.systemInfo = res
          this.getMessageHistory()
        }
      });
      this.$nextTick(() => {
        this.defaultPhoneHeight = window.innerHeight
        window.onresize = () => {
          console.log("window.onresize")
          this.nowPhoneHeight = window.innerHeight
          this.scrollContent()
        }
      })
    },
    convertStrList(str) {
      let str_list = str.split("```")
      str_list = str_list.map((item, index) => {
        if ((index + 1) % 2 != 0) return {type: 'text', data: item}
        else return {type: 'code', data: item}
      })
      return str_list
    },
    textItemClick(item, text) {
      if (item.title !== '使用示例') return
      this.inputValue = text.replace(/“/g, "")
    },
    getMessageHistory() {
      let {deviceId} = this.systemInfo
      http.get({url: '/openai/getMessageList', data: {uuid: deviceId}}).then(res => {
        if (res.data && res.data.length) this.showChatContent = true
        this.updateChatContent(res.data)
      })
    },
    sendMessage() {
      if (!this.inputValue) {
        this.$tools.showToast('请输入问题！')
        return
      }
      if (this.sendDisabled) return
      this.sendDisabled = true
      this.updateChatLayout({role: 'user', content: this.inputValue})
      this.updateChatLayout({role: 'assistant', content: "", flag: "last"})
      this.inputValue = ''
      this.showChatContent = true
      let {deviceId, uniPlatform, osName, deviceModel, deviceType} = this.systemInfo
      let platform = `uniPlatform:${uniPlatform};osName:${osName};deviceModel:${deviceModel};deviceType:${deviceType}`
      http.postBody({
        url: '/openai/chat',
        data: {
          uuid: deviceId,
          messages: this.chatList,
          ip_info: this.ipInfo,
          platform: platform,
        }
      }).then(res => {
        console.log("sendMessage==", res)
        this.sendDisabled = false
        let {code, msg, message: errMessage} = res
        let updateContent = `${msg || errMessage}` + (errMessage ? '，请稍后重试！' : '')
        if ((code == 1 && msg) || errMessage) this.$tools.showToast(msg || errMessage), this.updateLastChatContent(updateContent)
        let {data: {choices}} = res
        let message = choices && choices.length && choices[0].message
        if (message) this.updateChatLayout(message, true)
      }).catch(err => {
        this.$tools.showToast(err)
        this.sendDisabled = false
      })
    },
    updateChatContent(list) {
      this.messageList = [...this.messageList, ...list]
      this.$nextTick(() => this.chatScrollTop = this.chatScrollTop + 1)
    },
    updateChatLayout(message, removeLast = false) {
      if (message.flag !== 'last') this.chatList.push(message)
      if (removeLast) this.messageList.pop()
      this.messageList.push(message)
      this.$nextTick(() => this.chatScrollTop = this.chatScrollTop + 1)
    },
    updateLastChatContent(content) {
      this.messageList = this.messageList.map((item, index) => {
        if (index == this.messageList.length - 1) item.content = content, item.flag = ""
        return item
      })
    },
    scrollContent() {
      setTimeout(() => this.chatScrollTop = this.chatScrollTop + 1, 1000)
    },
    openDrawer() {
      this.$refs.drawer.open()
    },
    closeDrawer() {
      this.$refs.drawer.close()
    },
    settingItemClick(type) {
      if (type == 'mode') {
        let theme = uni.getStorageSync("theme")
        uni.setStorageSync('theme', theme == 'white' ? 'dark' : 'white')
        this.appTheme = uni.getStorageSync("theme")
        this.$refs.drawer.close()
      }
      if (type == 'question') {
        this.showChatContent = false
        this.$refs.drawer.close()
      }
      if (type == 'clear') {
        uni.showModal({
          content: "确定清除所有数据？",
        }).then(res => {
          this.$refs.drawer.close()
          if (!res[1].confirm) return
          this.messageList.splice(0, this.messageList.length)
          this.$tools.showToast("清除成功！")
        })
      }
    }
  }
}
</script>

<style scoped lang="scss">
.page-layout {
  width: 100%;
  min-height: calc(100vh - var(--top-window-height))
}

.title-layout {
  position: fixed;
  width: 100%;
  height: 44px;
  background: #000;
  z-index: 100;
}

.title-left-layout {
  z-index: 100;
  width: 50px;
  height: 100%;
}

.title-left-icon {
  width: 16px;
  height: 11px;
}

.title-view {
  position: absolute;
  width: 100%;
  text-align: center;
  font-size: 18px;
  font-family: PingFang SC;
  font-weight: 500;
  color: #fff;
}

.title-right-view {
  z-index: 100;
  font-size: 15px;
  font-family: PingFang SC;
  font-weight: 500;
  color: #fff;
  padding-right: 15px;
}

.demo-content-layout {
  margin-top: calc(44px + 10px);
  margin-bottom: 150px;
}

.main-title {
  font-size: 40px;
  font-family: PingFang;
  font-weight: 700;
  text-align: center;
  margin: 15px 0;
}

.demo-icon {
  width: 22px;
  height: 22px;
}

.text-item {
  width: 345px;
  background: #f5f5f5;
  border-radius: 2px;
  padding: 15px 0;
  margin: 0 auto 10px;
  font-size: 15px;
}

.darkinput {
  background: #545454 !important;
  border: none !important;
  box-shadow: none !important;
  color: #fff !important;
}

.nightbot {
  background: #262626 !important;
  border: none !important;
}

.nightinput {
  background: #545454 !important;
  border: none !important;
  box-shadow: none !important;
  color: #fff !important;
}

.text-icon {
  width: 14px;
  height: 5px;
  margin-left: 10px;
}

.bottom-chat-layout {
  position: fixed;
  bottom: 0px;
  width: 100%;
  padding: 25px 0;
  background: #f9f9f9;
  border: 0.5px solid #d2d2d2;
}

.chat-input {
  width: calc(100% - 50px);
  background: #fff;
  border: 0.5px solid #cacaca;
  box-shadow: 0 1px 8px 0 hsla(0, 0%, 89%, .39);
  border-radius: 5px;
  font-size: 15px;
  font-family: PingFang SC;
  font-weight: 500;
  color: #1a1818;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  padding: 15px;
}

.bottom-input-icon {
  position: absolute;
  right: 20px;
  width: 28px;
  height: 23px;
}

.bottom-input-icon:hover {
  opacity: 0.6;
}

.drawer-layout {
  width: 100%;
  height: 100%;
  background: #000;
  color: white;
}

.new-chat-layout {
  padding: 22px 17px 17px 12px;
  border-bottom: solid 0.5px #686868;
}

.new-chat-title {
  font-size: 20px;
  font-family: PingFang SC;
  font-weight: 500;
  color: #fffefe;
}

.add-icon {
  width: 14px;
  height: 14px;
}

.chat-questions {
  margin-top: 15px;
  margin-left: 20px;
  margin-right: 10px;
  padding-bottom: 5px;
  font-size: 15px;
  border-bottom: 1px solid #e1e1e1;
}

.drawer-setting-layout {
  padding: 0 10px;
  border-top: solid 0.5px #686868;
}

.drawer-setting-item {
  width: 278px;
  padding: 15px 0 5px 12px;
  display: flex;
  align-items: center;
}

.setting-icon {
  width: 15px;
  height: 15px;
}

.setting-text {
  font-size: 16px;
  font-family: PingFang SC;
  font-weight: 500;
  color: #fff;
  margin-left: 12px;
}

.content-layout {
  width: 100%;
}

.chat_content-layout {
  margin-top: calc(44px + 5px);
  height: calc(100vh - 44px - 5px - 100px - 8px);
}

.chat-item {
  margin-top: 20px;
  margin-bottom: 5px;
}

.chat-user-icon {
  flex-shrink: 0;
  width: 25px;
  height: 25px;
  border-radius: 50%;
  margin: 0 5px;
}

.message-time {
  background: #7b7878;
  color: white;
  width: fit-content;
  margin: 10px auto;
  font-size: 13px;
  padding: 3px 10px;
  border-radius: 4px;
}

.assistant-content {
  justify-content: flex-start;
}

.use-content {
  justify-content: flex-end;
}

.chat-content {
  white-space: pre-line;
  max-width: 285px;
  background: #f5f5f5;
  border: 0.5px solid #dbdbdb;
  border-radius: 2px;
  padding: 12px;
  font-size: 14px;
  font-family: PingFang SC;
  font-weight: 500;
  color: #3c3c3c;
  line-height: 1.5;
  word-wrap: break-word;
  -webkit-user-select: text;
  -ms-user-select: text;
  user-select: text; /* Standard syntax */
  //复制逻辑
  -webkit-user-select: auto;
  -moz-user-select: auto;
  user-select: auto;
}

.chat-code {
  background-color: #272822;
  color: #f8f8f2;
  padding: 16px;
  overflow: auto;
  font-size: 85%;
  line-height: 1.45;
  border-radius: 3px;
}

.chat-content.waiting::after {
  -webkit-animation: blink 1s steps(5, start) infinite;
  animation: blink 1s steps(5, start) infinite;
  content: "▋";
  margin-left: 0.25rem;
  vertical-align: baseline;
}

@-webkit-keyframes blink {
  to {
    visibility: hidden
  }
}

@keyframes blink {
  to {
    visibility: hidden
  }
}

/**
 * ==============================================
 * Dot Flashing
 * ==============================================
 */
.dot-flashing {
  //position: relative;
  position: absolute;
  right: 37px;
  width: 4px;
  height: 4px;
  border-radius: 2px;
  background-color: #333333;
  color: #333333;
  animation: dotFlashing 1s infinite linear alternate;
  animation-delay: .5s;
}

.dot-flashing::before, .dot-flashing::after {
  content: '';
  display: inline-block;
  position: absolute;
  top: 0;
}

.dot-flashing::before {
  left: -8px;
  width: 4px;
  height: 4px;
  border-radius: 2px;
  background-color: #333333;
  color: #333333;
  animation: dotFlashing 1s infinite alternate;
  animation-delay: 0s;
}

.dot-flashing::after {
  left: 8px;
  width: 4px;
  height: 4px;
  border-radius: 2px;
  background-color: #333333;
  color: #333333;
  animation: dotFlashing 1s infinite alternate;
  animation-delay: 1s;
}

@keyframes dotFlashing {
  0% {
    background-color: #333333;
  }
  50%,
  100% {
    background-color: #fffefe;
  }
}

@keyframes bounceInUp {

  from,
  60%,
  75%,
  90%,
  to {
    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  }

  from {
    opacity: 0;
    transform: translate3d(0, 100vh, 0);
  }

  60% {
    opacity: 1;
    transform: translate3d(0, -20px, 0);
  }

  75% {
    transform: translate3d(0, 10px, 0);
  }

  90% {
    transform: translate3d(0, -5px, 0);
  }

  to {
    transform: translate3d(0, 0, 0);
  }
}

.bounceInUp {
  animation-name: bounceInUp;
  animation-duration: 2s;
  animation-timing-function: linear;
}

.bounceInUpSoon {
  animation-name: bounceInUp;
  animation-duration: 1s;
  animation-timing-function: linear;
}
</style>
